Box2DSys = function(drawScale, debugDrawOn, ctx) {
    b2Vec2 = Box2D.Common.Math.b2Vec2;
    b2BodyDef = Box2D.Dynamics.b2BodyDef;
    b2Body = Box2D.Dynamics.b2Body;
    b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
    b2Fixture = Box2D.Dynamics.b2Fixture;
    b2World = Box2D.Dynamics.b2World;
    b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
    b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
    b2Shape = Box2D.Collision.Shapes.b2Shape;
    b2DebugDraw = Box2D.Dynamics.b2DebugDraw;

    this.bodies = [];
    this.gravity = null;
    this.world = null;
    this.drawScale = drawScale || 10;

    this.debugDrawOn = debugDrawOn || false;
    this.ctx = ctx;

    console.log("drawScale=" + this.drawScale);
};

/** Converts meters to pixels */
Box2DSys.prototype.mtp = function(meters) {
    return meters * this.drawScale;
};

Box2DSys.prototype.ptm = function(pixels) {
    return pixels / this.drawScale;
};

/**
 *  genera propiedades por defecto para un cuerpo.
 *  */
function DefaultProperties() {
    /*tipo dinamico de cuerpo es para aquellos a quienes se les aplica leyes de la fisica. estatico es para aquellos que no se moveran*/
    this.type = b2Body.b2_dynamicBody;
    this.linearDamping = 0.0;
    this.angularDamping = 0.0;
    this.fixedRotation = false;
    this.isBullet = false;
    this.density = 1.0;
    this.friction = 0.5;
    this.restitution = 0.5;
}//DefaultProperties

/**
 *  aplica impulso instantaneo sobre un cuerpo.
 *  */
Box2DSys.prototype.applyImpulse = function(body, degrees, power) {
    body.ApplyImpulse(new b2Vec2(Math.cos(degrees * (Math.PI / 180)) * power, Math.sin(degrees * (Math.PI / 180)) * power), body.GetWorldCenter());
};

Box2DSys.prototype.createCircle = function(x, y, radius, properties) {
    console.log("creating " + radius + " radius circle...");

    properties = ( typeof properties != "undefined" ? properties : new DefaultProperties);
    /*cuando se crea un nuevo objeto de js no es necesario introducir los parentesis*/
    var bodyDef = new b2BodyDef;
    bodyDef.type = properties.type;
    bodyDef.position.x = x;
    bodyDef.position.y = y;
    bodyDef.linearDamping = properties.linearDamping;
    bodyDef.angularDamping = properties.angularDamping;
    bodyDef.bullet = properties.isBullet;

    var fixDef = new b2FixtureDef;
    fixDef.shape = new b2CircleShape(radius);
    fixDef.density = properties.density;
    fixDef.friction = properties.friction;
    fixDef.restitution = properties.restitution;

    /*se le pide al mundo que cree un cuerpo nuevo a partir de una definicion superior.*/
    var body = this.world.CreateBody(bodyDef);

    /*se le asigna una forma al cuerpo creado.*/
    body.CreateFixture(fixDef);

    this.bodies.push(body);

    return body;
};
//createCircle

Box2DSys.prototype.createCircleFromGameObject = function(gameObject, properties) {
    return this.createCircle(this.ptm(gameObject.x), this.ptm(gameObject.y), this.ptm(gameObject.getAbsDimensions().h * 0.55), properties);
};

Box2DSys.prototype.createRectangle = function(x, y, width, height, properties) {
    properties = ( typeof properties != "undefined" ? properties : new DefaultProperties);
    /*cuando se crea un nuevo objeto de js no es necesario introducir los parentesis*/
    var bodyDef = new b2BodyDef;
    bodyDef.type = properties.type;
    bodyDef.position.x = x;
    bodyDef.position.y = y;
    bodyDef.linearDamping = properties.linearDamping;
    bodyDef.angularDamping = properties.angularDamping;
    bodyDef.bullet = properties.isBullet;

    var fixDef = new b2FixtureDef;
    fixDef.shape = new b2PolygonShape;
    //fixDef.shape = new b2CircleShape(1);
    fixDef.shape.SetAsBox(width, height);
    fixDef.density = properties.density;
    fixDef.friction = properties.friction;
    fixDef.restitution = properties.restitution;

    /*se le pide al mundo que cree un cuerpo nuevo a partir de una definicion superior.*/
    var body = this.world.CreateBody(bodyDef);

    /*se le asigna una forma al cuerpo creado.*/
    body.CreateFixture(fixDef);

    this.bodies.push(body);

    return body;
};
//createRectangle

Box2DSys.prototype.createRectangleFromGameObject = function(gameObject,properties){

}

Box2DSys.prototype.draw = function() {
    if (this.debugDrawOn)
        this.world.DrawDebugData();
};

/**
 * El mundo da un paso
 *  */
Box2DSys.prototype.step = function() {
    //console.log(mainCircle.GetPosition());
    this.world.Step(1 / GameSys.fps, 10, 10);

    this.draw();
};

Box2DSys.prototype.createGround = function(xi,yi,xf,yf) {
    var canvas=$("canvas")[0];

    xi=xi||0;
    yi=yi||this.ptm(canvas.height);
    xf=xf||this.ptm(canvas.width);
    yf=yf||this.ptm(canvas.height);

    var bodyDef = new b2BodyDef;
    var fixDef = new b2FixtureDef;
    fixDef.shape = new b2PolygonShape;
    /*PTM : pixelsToMeters*/
    fixDef.shape.SetAsEdge({
        x : 0,
        y : this.ptm(canvas.height)
    }, {
        x : this.ptm(canvas.width),
        y : this.ptm(canvas.height)
    });
    this.world.CreateBody(bodyDef).CreateFixture(fixDef);
};

Box2DSys.prototype.initBox2D = function(gravX, gravY) {
    var gy = gravY || 40;
    var gx = gravX || 0;

    this.gravity = new b2Vec2(gx, gy);
    // Sets the gravity of the world by using a vector
    this.world = new b2World(this.gravity, true);
    // Creates the world and sets the gravity.
    // The second parameter determines whether or not bodies can sleep

    if (this.debugDrawOn) {
        var debugDraw = new b2DebugDraw();
        debugDraw.SetSprite(this.ctx);
        debugDraw.SetDrawScale(this.drawScale);
        debugDraw.SetFillAlpha(0.3);
        debugDraw.SetLineThickness(1.0);
        debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
        this.world.SetDebugDraw(debugDraw);
    }

    /*canvas.addEventListener("click", handleMouseClick, false);
     canvas.addEventListener("mousemove", handleMouseMove, false);*/
    canvas.onselectstart = function() {
        return false;
    };
};

Box2DSys.prototype.run = function() {
    var mainCircle;
    var mainRect;

    var applyImpulse = this.applyImpulse.bind(this);

    var gy = 20;

    this.gravity = new b2Vec2(0, gy);
    // Sets the gravity of the world by using a vector
    this.world = new b2World(this.gravity, true);
    // Creates the world and sets the gravity.
    // The second parameter determines whether or not bodies can sleep

    this.createGround();

    /*canvas.addEventListener("click", handleMouseClick, false);
     canvas.addEventListener("mousemove", handleMouseMove, false);*/
    canvas.onselectstart = function() {
        return false;
    };

    //window.setInterval(this.step.bind(this), 1000 / GameSys.fps);

    var circleProperties = new DefaultProperties();
    circleProperties.restitution = 0;
    mainCircle = this.createCircle(5, this.ptm(canvas.height), 1, circleProperties);
    mainCircle.SetUserData("ball");

    var properties = new DefaultProperties();
    properties.type = b2Body.b2_staticBody;
    mainRect = this.createRectangle(this.ptm(canvas.width), this.ptm(canvas.height), 10, 10, properties);
    mainRect.SetUserData("wall");

    var box2dcont = new Box2DController(null, mainCircle, this);
    window.setInterval(box2dcont.run.bind(box2dcont), 1000 / GameSys.fps);

    var b2Listener = Box2D.Dynamics.b2ContactListener;

    //Add listeners for contact
    var listener = new b2Listener;

    listener.BeginContact = function(contact) {
        //console.log(contact.GetFixtureA().GetBody().GetUserData());
    };

    listener.EndContact = function(contact) {
        // console.log(contact.GetFixtureA().GetBody().GetUserData());
    };

    listener.PostSolve = function(contact, impulse) {
        if (contact.GetFixtureA().GetBody().GetUserData() == 'wall' || contact.GetFixtureB().GetBody().GetUserData() == 'wall') {
            console.log("contact between ball and wall");
            // var impulse = impulse.normalImpulses[0];
            // if (impulse < 0.2)
            // return;
            // world.ball.impulse = impulse > 0.6 ? 0.5 : impulse;
            // console.log(world.ball.impulse);
        }
    };

    listener.PreSolve = function(contact, oldManifold) {
        // PreSolve
    };

    this.world.SetContactListener(listener);
};
//run
